package com.just4e;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 把java源代码转化为html页面.页面的结构和包结构一样<br>
 *FIXME:链接中对于同名的类没有处理
 */
public class Convter {

	/**
	 * java关键字
	 */
	private static final String KEY_WORDS = "break,byte,boolean,catch,case,class,char,continue,default,double,do,else,enum,extends,false,final,float,for,finally,if,import,implements,int,interface,instanceof,long,length,native,new,null,package,private,protected,public,final,return,switch,synchronized,short,static,super,try,true,this,throw,throws,threadsafe,transient,void,volatile,while";

	/**
	 * 关键字的数组,此数组经过自然排序
	 */
	private static String[] keys = null;

	/**
	 * 换行
	 */
	private static final String NL = System.getProperty("line.separator");

	/**
	 * 记录所有类文件的名称.
	 */
	private List<String> classes;

	/**
	 * 包名集合,有序
	 */
	private Set<String> packages;

	/**
	 * 项目名称
	 */
	private String project;

	private Map<String, String> classesmap = new HashMap<String, String>();

	static {
		keys = KEY_WORDS.split(",");
		Arrays.sort(keys);
	}

	public Convter(String project) {
		classes = new ArrayList<String>();
		this.project = project;
	}

	/**
	 * 开始转化
	 * 
	 * @param srcRoot
	 *            转化的源根路径.必须是一个文件夹.
	 * @param targetRoot
	 *            转化到目标的根路径,css文件就放在此目录下
	 * @throws IOException
	 */
	public void mirror(String srcRoot, String targetRoot) throws IOException {
		File src = new File(srcRoot);
		if (!src.exists()) {
			throw new FileNotFoundException(srcRoot + "不存在");
		}

		createClassesMap(src);

		if (!src.isDirectory()) {
			throw new IOException(srcRoot + "不是一个文件夹");
		}
		File target = new File(targetRoot);
		if (!target.exists()) {
			target.mkdirs();
		}
		String srcAP = src.getAbsolutePath();
		for (File f : src.listFiles()) {
			walk(srcAP, target, f, 1);
		}
		index(target);
	}

	private void createClassesMap(File srcfile) {
		if (srcfile.isDirectory()) {
			File[] files = srcfile.listFiles();
			for (File file : files) {
				createClassesMap(file);
			}
		} else {
			String filename = srcfile.getName();
			if (filename.endsWith("java")) {
				try {
					BufferedReader reader = new BufferedReader(
							new InputStreamReader(new FileInputStream(srcfile),
									"UTF-8"));
					String line = null;

					while ((line = reader.readLine()) != null) {
						line = line.trim();
						if (line.startsWith("package")) {
							filename = filename.substring(0, filename
									.indexOf("."));
							classesmap.put(filename, line.substring(
									line.indexOf(" ") + 1, line.indexOf(";"))
									.replaceAll("\\.", "/")
									+ "/" + filename + ".html");
							break;
						}
					}

					reader.close();
				} catch (UnsupportedEncodingException e) {
					e.printStackTrace();
				} catch (FileNotFoundException e) {
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 生成目录索引和其他入口相关页面
	 * 
	 * @param target
	 *            目标的根目录
	 */
	private void index(File target) {
		Collections.sort(classes);// 自然排序一下
		packages = new LinkedHashSet<String>();
		for (String s : classes) {
			packages.add(s.substring(0, s.lastIndexOf("/")));
		}
		// 生成根文件夹下所需文件
		writeCSS(target);
		writeRootHTML(target, "index.html");
		writeRootHTML(target, "overview-frame.html");
		writeRootHTML(target, "overview-summary.html");
		writeRootHTML(target, "allclasses-frame.html");

		indexPackages(target);
	}

	/**
	 * 生成子包下的索引页面
	 * 
	 * @param target
	 *            目标的根目录
	 */
	private void indexPackages(File target) {
		for (String p : packages) {
			File dir = new File(target, p);
			// java名,无包名,无后缀
			List<String> javas = new ArrayList<String>();
			for (String name : classes) {// 这里的处理不好...
				if (name.startsWith(p)) {// 需要进一步判断
					if (name.substring(0, name.lastIndexOf("/")).equals(p)) {
						String java = name.substring(name.lastIndexOf("/") + 1,
								name.lastIndexOf("."));
						javas.add(java);
					}
				}
			}
			Collections.sort(javas);

			writeChildHTML(dir, javas, p, "package-frame.html");
			writeChildHTML(dir, javas, p, "package-summary.html");
		}
	}

	private void writeChildHTML(File dir, List<String> javas, String pkName,
			String name) {
		try {
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					this.getClass().getResourceAsStream(name), "UTF-8"));
			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(new File(dir, name)), "UTF-8"));
			String line = null;
			String realName = pkName.replace("/", ".");
			String path = "";
			for (int i = 0; i < pkName.split("/").length; i++) {
				path += "../";
			}
			while ((line = reader.readLine()) != null) {
				if (line.contains("{name}")) {// 替换项目名称
					line = line.replace("{name}", project);
				}
				if (line.contains("{package}")) {
					line = line.replace("{package}", realName);
				}
				if (line.contains("{level}")) {
					line = line.replace("{level}", path);
				}
				if (line.contains("{ul}")) {
					StringBuilder sb = new StringBuilder();
					sb.append("<ul>");
					for (String s : javas) {
						sb.append("<li>");
						sb.append("<a href=\"");
						sb.append(s);
						sb.append(".html\" target=\"classFrame\">");
						sb.append(s);
						sb.append("</a>");
						sb.append("</li>");
						sb.append(NL);
					}
					sb.append("</ul>");
					line = sb.toString();
				}
				if (line.contains("{tr}")) {
					StringBuilder sb = new StringBuilder();
					for (String s : javas) {
						sb.append("<tr>");
						sb.append("<td>");
						sb.append("<a href=\"");
						sb.append(s);
						sb.append(".html\" target=\"classFrame\">");
						sb.append(s);
						sb.append("</a>");
						sb.append("</td>");
						sb.append("</tr>");
						sb.append(NL);
					}
					line = sb.toString();
				}

				writer.write(line);
				writer.write(NL);
			}
			writer.flush();
			writer.close();
			reader.close();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	private void writeRootHTML(File target, String name) {
		try {
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					this.getClass().getResourceAsStream(name), "UTF-8"));
			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(new File(target, name)), "UTF-8"));
			String line = null;
			while ((line = reader.readLine()) != null) {
				if (line.contains("{name}")) {// 替换项目名称
					line = line.replace("{name}", project);
					writer.write(line);
				} else if (line.contains("{ul}")) {
					// 生成overview-frame.html中的包名列表
					StringBuilder sb = new StringBuilder();
					sb.append("<ul>");
					for (String s : packages) {
						sb.append("<li>");
						sb.append("<a href=\"");
						sb.append(s);
						sb
								.append("/package-frame.html\" target=\"packageFrame\">");
						sb.append(s.replace("/", "."));
						sb.append("</a>");
						sb.append("</li>");
						sb.append(NL);
					}
					sb.append("</ul>");
					writer.write(sb.toString());
				} else if (line.contains("{tr}")) {
					// 生成overview-summary.html中的包名列表
					StringBuilder sb = new StringBuilder();
					for (String s : packages) {
						sb.append("<tr>");
						sb.append("<td>");
						sb.append("<a href=\"");
						sb.append(s);
						sb.append("/package-summary.html\">");
						sb.append(s.replace("/", "."));
						sb.append("</a>");
						sb.append("</td>");
						sb.append("</tr>");
						sb.append(NL);
					}
					writer.write(sb.toString());
				} else if (line.contains("{all}")) {
					// 生成allclasses-frame.html中的所有类名列表
					StringBuilder sb = new StringBuilder();
					sb.append("<ul>");
					for (String s : classes) {
						sb.append("<li>");
						sb.append("<a href=\"");
						sb.append(s.substring(0, s.lastIndexOf(".")) + ".html");
						sb.append("\" target=\"classFrame\">");
						sb.append(s.substring(s.lastIndexOf("/") + 1, s
								.lastIndexOf(".")));
						sb.append("</a>");
						sb.append("</li>");
						sb.append(NL);
					}
					sb.append("</ul>");
					writer.write(sb.toString());
				} else {
					writer.write(line);
				}
				writer.write(NL);
			}
			writer.flush();
			writer.close();
			reader.close();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 复制css文件
	 * 
	 * @param target
	 */
	private void writeCSS(File target) {
		InputStream is = this.getClass().getResourceAsStream("stylesheet.css");
		File css = new File(target, "stylesheet.css");
		copy(is, css);
	}

	/**
	 * 遍历文件夹
	 * 
	 * @param srcAP
	 *            源的根的绝对路径
	 * @param target
	 *            目标的根文件夹
	 * @param file
	 *            遍历到的文件
	 * @param level
	 *            遍历的层级.对于根下目录来说,是第一层,向下依次+1
	 */
	private void walk(String srcAP, File target, File file, int level) {
		if (file.isDirectory()) {
			String p = file.getAbsolutePath();
			String n = p.substring(srcAP.length());
			File d = new File(target, n);
			if (!d.exists()) {// 不存在,新建
				d.mkdirs();
			}
			for (File f : file.listFiles()) {
				walk(srcAP, target, f, level + 1);
			}
		} else {
			String path = file.getAbsolutePath();
			String n = path.substring(srcAP.length());
			if (path.endsWith(".java")) {// java,转化
				String clazz = n.replace(File.separator, "/");// 将win下的\\替换为/
				if (clazz.startsWith("/")) {
					clazz = clazz.substring(1);
				}
				classes.add(clazz);
				n = n.substring(0, n.lastIndexOf(".")) + ".html";
				File html = new File(target, n);
				convert(file, html, level);
			} else {
				File t = new File(target, n);
				copy(file, t);
			}
		}
	}

	/**
	 * 转化java文件到html文件
	 * 
	 * @param java
	 *            java文件
	 * @param html
	 *            html文件
	 * @param level
	 *            文件层级
	 */
	private void convert(File java, File html, int level) {
		try {
			String name = java.getName();
			BufferedReader reader = new BufferedReader(new InputStreamReader(
					new FileInputStream(java), "UTF-8"));
			BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(html), "UTF-8"));

			StringBuilder sbf = new StringBuilder();

			sbf
					.append(
							"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">")
					.append(
							"<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en_US\" lang=\"en_US\">")
					.append("<head>")
					.append(
							"<meta http-equiv=\"content-type\" content=\"text/html; charset=UTF-8\" />")
					.append("<title>")
					.append(name.substring(0, name.lastIndexOf(".")))
					.append(" 在线源码</title>")
					.append(
							"<link type=\"text/css\" rel=\"stylesheet\" href=\"");
			String prefix = "";
			for (int i = 1; i < level; i++) {
				prefix += "../";// 设置相对路径,这个是指向根目录下.css文件就放在根下
			}

			sbf.append(prefix);
			sbf.append("stylesheet.css\" />").append("</head>")
					.append("<body>").append("<pre>").append(NL);
			sbf.append("<ul class=\"num\">");

			String line = null;
			int index = 1;// 代码行
			int comment = 0;// //1=/*注释,2=/**注释,0=无注释

			while ((line = reader.readLine()) != null) {
				sbf.append("<li><a class=\"jxr_linenumber\" name=\"").append(
						index).append("\" href=\"#").append(index).append(
						"\"></a>  ");
				// 替换html中的<>,防止干扰
				line = line.replaceAll("<", "&lt;");
				line = line.replaceAll(">", "&gt;");

				String trim = line.trim();
				if (trim.startsWith("/**")) {
//					comment = 2;// doc注释
					comment = 1;  // 接受 键盘上跳舞 意见，将注释都改成 类似于 javadoccomment
				} else if (trim.startsWith("/*")) {
					comment = 1;// 多行注释
				}
				if (comment > 0 || trim.startsWith("//")) {
					if (comment == 2) {
						sbf.append("<em class=\"jxr_javadoccomment\">");
					} else {
						sbf.append("<em class=\"jxr_comment\">");
					}
					sbf.append(line);
					sbf.append("</em>");
				} else {// 代码行
					int length = line.length();
					StringBuilder sb = new StringBuilder(length);
					for (int i = 0; i < length; i++) {
						char c = line.charAt(i);
						if (isEffective(c)) {
							int j = i + 1;
							for (; j < length; j++) {
								char nc = line.charAt(j);
								if (!isEffective(nc)) {
									break;
								}
							}
							String word = line.substring(i, j);
							if (Arrays.binarySearch(keys, word) >= 0) {// 关键字
								sb.append("<strong class=\"jxr_keyword\">");
								sb.append(word);
								sb.append("</strong>");
							} else if (classesmap.get(word) != null) {// 关联类
								sb.append("<a href=\"");
								sb.append(prefix);
								sb.append(classesmap.get(word));
								sb.append("\">").append(word).append("</a>");
							} else {
								sb.append(word);
							}
							i = j - 1;
						} else {
							sb.append(c);
						}
					}
					sbf.append(sb.toString());
				}
				if (trim.endsWith("*/")) {
					comment = 0;
				}
				sbf.append("</li>");
				sbf.append(NL);
				index++;
			}
			sbf.append("</ul></pre>");
			sbf
					.append("<hr/><div id=\"footer\">感谢DOMO、HUPY提供技术支持  感谢QQ群144615883 成员的支持 <a target=\"_blank\" href=\"http://www.just4e.com\">Just4e</a></div></body>");
			sbf.append("<script type=\"text/javascript\">");
			sbf
					.append("var _bdhmProtocol = ((\"https:\" == document.location.protocol) ? \" https://\" : \" http://\");");
			sbf
					.append("document.write(unescape(\"%3Cscript src='\" + _bdhmProtocol + \"hm.baidu.com/h.js%3Fd5e7a42f848de89069733d940881fa90' type='text/javascript'%3E%3C/script%3E\"));");
			sbf.append("</script>");
			sbf.append("</html>");

			writer.write(sbf.toString());

			writer.flush();
			writer.close();
			reader.close();
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 非java文件,复制
	 * 
	 * @param src
	 *            源文件
	 * @param target
	 *            目标文件
	 */
	private void copy(File src, File target) {
		try {
			InputStream is = new FileInputStream(src);
			copy(is, target);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 流对流的copy,此方法会关闭所有流
	 * 
	 * @param is
	 *            输入流
	 * @param os
	 *            输出流
	 */
	private void copy(InputStream is, File target) {
		try {
			OutputStream os = new FileOutputStream(target);
			byte[] b = new byte[1024 * 10];
			int length = -1;
			while ((length = is.read(b)) != -1) {
				os.write(b, 0, length);
			}
			os.flush();
			os.close();
			is.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 判断是否是英文字符和数字
	 * 
	 * @param c
	 *            字符
	 * @return 是返回true
	 */
	private boolean isEffective(char c) {
		return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c >= '0'
				&& c <= '9';
	}

	public static void main(String[] args) {
		try {
			new Convter("Jsonp").mirror("E:\\Jar\\jsoup\\jsoup-1.5.2-sources",
					"E:\\Jar\\jsoup\\xref");
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}
